#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <linux/fs.h>
#include <linux/cdev.h>

MODULE_LICENSE("Dual BSD/GPL");

#define HELLO_MAGIC 'k'
#define HELLO_CMD1    _IO(HELLO_MAGIC,0x1a)
#define HELLO_CMD2    _IO(HELLO_MAGIC,0x1b)

static int hello_major = 0;		/* major device number */
static struct cdev hellow;	/* hello device structure */

static int hello_open( struct inode *inode, struct file *filp )
{
	printk( KERN_NOTICE"Hello device open!\n" );
	return 0;
}

static int hello_release( struct inode *inode, struct file *filp )
{
	printk( KERN_NOTICE"Hello device close!\n" );
	return 0;
}

ssize_t hello_read( struct file *flip, char __user *buf, size_t count,loff_t*f_pos)
{
	ssize_t retval = 0;
	char *bank;

	bank = kmalloc(count+1, GFP_KERNEL );
	if( bank == NULL )
		return -1;

	memset( bank, 'A',count );
	if( copy_to_user( buf, bank, count ) ){
		retval = -EFAULT;
		goto out;
	}
	retval += count;
	*(bank+count)=0;
	printk( KERN_NOTICE"hello: user read %d bytes from me. %s\n",count,bank );
out:
	kfree(bank);
	return retval;
}

ssize_t hello_write( struct file *filp, const char __user *buf, size_t count,loff_t *f_pos )
{
		ssize_t retval = 0;
		char *bank = kmalloc( count ,GFP_KERNEL );
		if( bank == NULL )
			return retval;

		if( copy_from_user(bank, buf, count ) ){
			retval = -EFAULT;
			printk( KERN_NOTICE"hello: write error\n" );
			goto out;
		}

		retval += count;
	printk( KERN_NOTICE"hello: user has written %d bytes to me: %s\n",count,bank );
out:
		kfree(bank );
		return retval;
}

static int hello_ioctl( struct file *file,unsigned int cmd, unsigned long arg)
{
	int temp = 0;
	switch(cmd)
	{

	case HELLO_CMD1:
	{
		temp = 1;
		if(copy_to_user( (int *)arg, &temp, sizeof(int))) return -EFAULT;
		break;
	}
	case HELLO_CMD2:
	{
		temp = 2;
		if(copy_to_user( (int *)arg, &temp, sizeof(int))) return -EFAULT;
		break;
	}
}
	printk( KERN_NOTICE"ioctl CMD%d done!\n",temp);

	return 0;
}

static struct file_operations hello_ops = {
	.owner = THIS_MODULE,
	.unlocked_ioctl = hello_ioctl,
	.open = hello_open,
	.read = hello_read,
	.write = hello_write,
	.release = hello_release,
};

static void hello_setup_cdev( struct cdev *dev, int minor, struct file_operations *fops )
{
	int err;
	int devno = MKDEV( hello_major, minor );
	/* initialize the cdev struct */
	cdev_init( dev,fops );
	dev->owner = THIS_MODULE;
	err = cdev_add( dev, devno, 1 ); /* register the cdev in the kernel */
	if( err )
		printk( KERN_NOTICE"Error %d adding hello%d\n",err ,minor );
}

static int hello_init(void)
{
	int result;
	dev_t dev = MKDEV( hello_major, 0 );/*to transfer major as dev_t type*/

	/* alloc the major	device number dynamicly */
	result = alloc_chrdev_region(&dev, 0 ,1, "hello" );
	if( result < 0 ){
		printk( KERN_NOTICE"Hello: unable to get major %d\n",hello_major );
	return result;
	}

	hello_major = MAJOR(dev);
	/* set up devices, in this case, there is only one device */
	printk( KERN_NOTICE"hello init. major:%d, minor:%d\n",hello_major,0 );
	//printk( KERN_ALERT"hello init: %d, %d\n",hello_major,0 );
	hello_setup_cdev(&hellow, 0 , &hello_ops );

	return 0;
}

static void hello_exit(void)
{
	cdev_del(&hellow);
	/* release the device numble alloced earlier */
	unregister_chrdev_region( MKDEV( hello_major, 0 ), 1 );
	printk( KERN_NOTICE"hello exit. major:%d,minor %d\n",hello_major,0 );
}

module_init(hello_init);
module_exit(hello_exit);
